import { computed, inject, ref, Ref, watch } from 'vue'

/**
 *
 * @class RuleValidator
 */
export default {
  setup(props: { code: any; model: { code: any, rules: any, default: any} }, context: any) {
    const form: any = inject('form')
    const validInstance: Ref<any> = ref(null)
    const initVal: Ref<any> = ref(undefined)
    
    const code = computed(() => props.code)
    const status = computed(() => form.fieldStatus[code.value])

    watch(() => props.model, (v) => {
      if (v && v.rules && v.rules.length > 0) validInstance.value = new RuleValidator(v.rules)
    }, {
      immediate: true,
      deep: true,
    })

    const validate = () => {
      return new Promise(resolve => {
        if (validInstance.value === null) {
          resolve({ valid: true })
          return
        }
        try {
          validInstance.value.commonValidator(form.modelValue, props.model)
          status.value && !status.value.valid && form.setFieldStatus(props.model.code, 'valid', true)
          resolve({ valid: true })
        } catch (err: any) {
          form.setFieldStatus(props.model.code, 'valid', false)
          form.setFieldStatus(props.model.code, 'errorMessage', `${err.message}`)
          resolve({ valid: false, message: `${err.message}` })
        }
      })
    }
    const blurValidator = () => {
      context.emit('on-blur')
      if (validInstance.value === null) return
      try {
        validInstance.value.blurValidator(form.modelValue, props.model)
        status.value && !status.value.valid && form.setFieldStatus(props.model.code, 'valid', true)
      } catch (err: any) {
        form.setFieldStatus(props.model.code, 'valid', false)
        form.setFieldStatus(props.model.code, 'errorMessage', `${err.message}`)
      }
    }
    const changeValidator = (value: any) => {
      context.emit('on-change', value)
      if (validInstance.value === null) return
      try {
        validInstance.value.changeValidator(value)
        status.value && !status.value.valid && form.setFieldStatus(props.model.code, 'valid', true)
      } catch (err: any) {
        form.setFieldStatus(props.model.code, 'valid', false)
        form.setFieldStatus(props.model.code, 'errorMessage', `${err.message}`)
      }
    }
    const reset = () => {
      const dictionary = { Number: null, String: '', Boolean: null, Array: [], Undefined: undefined, Null: null }
      const type = Object.prototype.toString.call(form.value[props.model.code])
      // @ts-ignore
      let value = dictionary[type.substring(8, type.length - 1)]
      if (props.model.default) value = props.model.default
      if (initVal.value !== undefined) value = initVal.value
      status.value && !status.value.valid && form.setFieldStatus(props.model.code, 'valid', true)
      form.inputValue(props.model.code, value)
      // if(this.$options.name === 'fileUpload') {
      //   this.$refs.upload.clearFiles()
      // }
    }
    interface RegListModel {
      phone: RegExp
      email: RegExp
    }
    class RuleValidator {
      // range required pattern
      regList: RegListModel = {
        phone: /^1[3456789]\d{9}$/,
        email: /^[A-Za-z0-9\u4e00-\u9fa5]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$/
      }
      rules: any
      blurRules: any[]
      changeRules: any[]
    
      constructor (rules: any) {
        this.rules = rules
        this.blurRules = []
        this.changeRules = []
        // console.log(this.rules)
        this.rules.forEach((v: { trigger: string }) => {
          v.trigger === 'blur' && this.blurRules.push(v)
          v.trigger === 'change' && this.changeRules.push(v)
        })
      }
    
      static empty (value: string | any[] | null | undefined) {
        return value === '' || value === null || value === undefined || (Array.isArray(value) && value.length === 0)
      }
    
      commonValidator (value: any, model: any) {
        if (this.rules.length === 0) return
        this.validator(value, this.rules, model)
      }
    
      blurValidator (value: any) {
        if (this.blurRules.length === 0) return
        this.validator(value, this.blurRules)
      }
    
      changeValidator (value: any) {
        if (this.changeRules.length === 0) return
        this.validator(value, this.changeRules)
      }
    
      validator (value: any, rules: any, model?: any) {
        rules.forEach((v: { required: any; message: any; type: string; max: number; min: number; pattern: string | RegExp; validator: (arg0: { formData: any; vm: any; value: any; rule: any; callback: (res: any) => void }) => void }) => {
          if (v.required) {
            if (RuleValidator.empty(value)) throw new Error(`${v.message || (model.name + '不能为空')}`)
          }
          // 如果值为空下面几个规则不作校验
          if (RuleValidator.empty(value)) return
          if (v.type === 'range') {
            const size = typeof value === 'number' ? value : value.length
            if (v.max && size > v.max) throw new Error(`${v.message || ''}`)
            if (v.min && size < v.min) throw new Error(`${v.message || ''}`)
            return
          }
          if (this.regList[v.type as keyof RegListModel]) {
            console.log(typeof this.regList[v.type as keyof RegListModel])
            if (!this.regList[v.type as keyof RegListModel].test(value)) throw new Error(`${v.message || ''}`)
            return
          }
          if (v.pattern) {
            const reg = Object.prototype.toString.call(v.pattern).includes('RegExp') ? v.pattern : new RegExp(v.pattern)
            // @ts-ignore
            if (!reg.test(value)) throw new Error(`${v.message || ''}`)
            return
          }
          if (v.validator) {
            if (typeof v.validator !== 'function') return
            const callback = function (res: { message: any }) {
              if (Object.prototype.toString.call(res).includes('Error')) throw new Error(`${res.message || v.message || ''}`)
            }
            /**
             * 自定义的validator包含参数
             * formData:此表单所有字段数据、vm:当前control组件实例、value:当前字段值、
             * rule:本条校验规则对象、callback:回调函数
             * callback(new Error('错误信息'))
             */
            const obj = { formData: form.value, vm: context, value: value, rule: v, callback: callback }
            v.validator(obj)
          }
        })
      }
    }
  }
}
